"""
请求验证中间件
提供请求ID追踪、速率限制、请求大小限制等功能
"""

import time
import uuid
import json
import traceback
from typing import Dict, Optional, Set, Callable
from collections import defaultdict, deque
from fastapi import Request, Response, HTTPException, status
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

from app.core.logging import get_logger
from app.core.config import settings

logger = get_logger(__name__)


class RateLimiter:
    """简单的内存速率限制器"""
    
    def __init__(self):
        self.requests: Dict[str, deque] = defaultdict(deque)
        self.blocked_ips: Dict[str, float] = {}
    
    def is_allowed(
        self,
        identifier: str,
        max_requests: int = 100,
        window_seconds: int = 60,
        block_duration: int = 300
    ) -> bool:
        """
        检查是否允许请求
        
        Args:
            identifier: 客户端标识符（IP地址）
            max_requests: 时间窗口内最大请求数
            window_seconds: 时间窗口（秒）
            block_duration: 封禁时长（秒）
            
        Returns:
            bool: 是否允许请求
        """
        current_time = time.time()
        
        # 检查是否在封禁期内
        if identifier in self.blocked_ips:
            if current_time - self.blocked_ips[identifier] < block_duration:
                return False
            else:
                # 封禁期结束，移除封禁记录
                del self.blocked_ips[identifier]
        
        # 清理过期的请求记录
        request_times = self.requests[identifier]
        while request_times and current_time - request_times[0] > window_seconds:
            request_times.popleft()
        
        # 检查是否超过速率限制
        if len(request_times) >= max_requests:
            # 封禁该IP
            self.blocked_ips[identifier] = current_time
            logger.warning(f"IP {identifier} 被封禁，原因：超过速率限制")
            return False
        
        # 记录当前请求
        request_times.append(current_time)
        return True
    
    def get_remaining_requests(self, identifier: str, max_requests: int = 100) -> int:
        """获取剩余请求数"""
        current_requests = len(self.requests[identifier])
        return max(0, max_requests - current_requests)


class RequestValidationMiddleware(BaseHTTPMiddleware):
    """请求验证中间件"""
    
    def __init__(self, app, rate_limiter: Optional[RateLimiter] = None):
        super().__init__(app)
        self.rate_limiter = rate_limiter or RateLimiter()
        
        # 配置参数 - 添加更安全的默认值
        self.max_request_size = getattr(settings, 'MAX_REQUEST_SIZE', None)
        # 如果MAX_REQUEST_SIZE为None，使用文件上传配置中的最大文件大小
        if self.max_request_size is None:
            # 安全地获取文件上传配置
            file_upload_settings = getattr(settings, 'file_upload', None)
            if file_upload_settings is not None:
                self.max_request_size = getattr(file_upload_settings, 'MAX_FILE_SIZE', 10 * 1024 * 1024)
            else:
                self.max_request_size = 10 * 1024 * 1024  # 默认10MB
        
        self.rate_limit_enabled = getattr(settings, 'RATE_LIMIT_ENABLED', True)
        self.rate_limit_requests = getattr(settings, 'RATE_LIMIT_REQUESTS', 100)
        self.rate_limit_window = getattr(settings, 'RATE_LIMIT_WINDOW', 60)
        
        # 不需要速率限制的路径
        self.rate_limit_exempt_paths: Set[str] = {
            "/health",
            "/docs",
            "/redoc",
            "/openapi.json",
            "/favicon.ico"
        }
        
        # 静态文件路径前缀
        self.static_prefixes = {"/static", "/assets", "/images"}
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """处理HTTP请求"""
        request_id = str(uuid.uuid4())
        request.state.request_id = request_id
        
        # 获取客户端IP
        client_ip = self._get_client_ip(request)
        request.state.client_ip = client_ip
        
        try:
            # 记录请求开始
            logger.info(
                "请求处理开始",
                request_id=request_id,
                path=request.url.path,
                method=request.method,
                client_ip=client_ip
            )
            
            # 验证请求大小
            logger.info("验证请求大小", request_id=request_id)
            if not await self._validate_request_size(request):
                logger.warning("请求体过大", request_id=request_id)
                return self._create_error_response(
                    status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                    "请求体过大",
                    "REQUEST_TOO_LARGE",
                    request_id
                )
            
            # 验证请求头
            logger.info("验证请求头", request_id=request_id)
            if not self._validate_headers(request):
                logger.warning("请求头验证失败", request_id=request_id)
                return self._create_error_response(
                    status.HTTP_400_BAD_REQUEST,
                    "请求头验证失败",
                    "INVALID_HEADERS",
                    request_id
                )
            
            # 检查速率限制
            logger.info("检查速率限制", request_id=request_id)
            if not self._check_rate_limit(request, client_ip):
                logger.warning("请求频率过高", request_id=request_id)
                return self._create_error_response(
                    status.HTTP_429_TOO_MANY_REQUESTS,
                    "请求频率过高，请稍后再试",
                    "RATE_LIMIT_EXCEEDED",
                    request_id
                )
            
            # 记录请求日志
            logger.info("调用下一个中间件", request_id=request_id)
            response = await call_next(request)
            logger.info("下一个中间件调用完成", request_id=request_id)
            
            # 记录请求日志
            self._log_request(request, response, time.time(), request_id)
            
            return response
            
        except Exception as e:
            logger.error(
                "请求处理异常",
                error=str(e),
                request_id=request_id,
                path=request.url.path,
                method=request.method,
                client_ip=client_ip,
                traceback_str=traceback.format_exc()
            )
            
            return self._create_error_response(
                status.HTTP_500_INTERNAL_SERVER_ERROR,
                "服务器内部错误",
                "INTERNAL_ERROR",
                request_id
            )
    
    def _get_client_ip(self, request: Request) -> str:
        """获取客户端真实IP"""
        # 检查代理头
        forwarded_for = request.headers.get("X-Forwarded-For")
        if forwarded_for:
            # 取第一个IP（原始客户端IP）
            return forwarded_for.split(",")[0].strip()
        
        real_ip = request.headers.get("X-Real-IP")
        if real_ip:
            return real_ip
        
        # 回退到直接连接IP
        if request.client:
            return request.client.host
        
        return "unknown"
    
    async def _validate_request_size(self, request: Request) -> bool:
        """验证请求大小"""
        logger.info("进入验证请求大小函数")
        content_length = request.headers.get("content-length")
        logger.info(f"Content-Length头: {content_length}")
        if content_length:
            try:
                size = int(content_length)
                logger.info(f"请求大小: {size} bytes, 最大允许: {self.max_request_size} bytes")
                # 确保max_request_size是整数
                max_size = self.max_request_size if isinstance(self.max_request_size, int) else 10 * 1024 * 1024
                if size > max_size:
                    logger.warning(
                        f"请求体过大: {size} bytes, 最大允许: {max_size} bytes",
                        path=request.url.path,
                        client_ip=getattr(request.state, 'client_ip', 'unknown')
                    )
                    return False
            except ValueError as e:
                logger.error(f"Content-Length转换失败: {e}")
                return False
        
        logger.info("请求大小验证通过")
        return True
    
    def _validate_headers(self, request: Request) -> bool:
        """验证请求头"""
        logger.info("进入验证请求头函数")
        # 检查User-Agent（可选）
        user_agent = request.headers.get("user-agent", "")
        logger.info(f"User-Agent长度: {len(user_agent)}")
        if len(user_agent) > 500:  # 防止过长的User-Agent
            logger.warning("User-Agent过长")
            return False
        
        # 检查Content-Type（对于POST/PUT请求）
        if request.method in ["POST", "PUT", "PATCH"]:
            content_type = request.headers.get("content-type", "")
            logger.info(f"Content-Type: {content_type}")
            if content_type and not self._is_valid_content_type(content_type):
                logger.warning(f"无效的Content-Type: {content_type}")
                return False
        
        logger.info("请求头验证通过")
        return True
    
    def _is_valid_content_type(self, content_type: str) -> bool:
        """验证Content-Type"""
        allowed_types = {
            "application/json",
            "application/x-www-form-urlencoded",
            "multipart/form-data",
            "text/plain",
            "application/xml",
            "text/xml"
        }
        
        # 提取主要的content-type（忽略charset等参数）
        main_type = content_type.split(";")[0].strip().lower()
        return main_type in allowed_types
    
    def _check_rate_limit(self, request: Request, client_ip: str) -> bool:
        """检查速率限制"""
        if not self.rate_limit_enabled:
            return True
        
        # 检查是否为免速率限制的路径
        path = request.url.path
        if path in self.rate_limit_exempt_paths:
            return True
        
        # 检查是否为静态文件
        if any(path.startswith(prefix) for prefix in self.static_prefixes):
            return True
        
        # 执行速率限制检查
        return self.rate_limiter.is_allowed(
            client_ip,
            self.rate_limit_requests,
            self.rate_limit_window
        )
    
    def _log_request(
        self,
        request: Request,
        response: Response,
        start_time: float,
        request_id: str
    ):
        """记录请求日志"""
        duration = time.time() - start_time
        
        # 只记录非静态资源的请求
        path = request.url.path
        if any(path.startswith(prefix) for prefix in self.static_prefixes):
            return
        
        log_data = {
            "request_id": request_id,
            "method": request.method,
            "path": path,
            "status_code": response.status_code,
            "duration": round(duration, 3),
            "client_ip": getattr(request.state, 'client_ip', 'unknown'),
            "user_agent": request.headers.get("user-agent", "")[:100]  # 截断过长的UA
        }
        
        # 根据状态码选择日志级别
        if response.status_code >= 500:
            logger.error("HTTP请求", **log_data)
        elif response.status_code >= 400:
            logger.warning("HTTP请求", **log_data)
        else:
            logger.info("HTTP请求", **log_data)
    
    def _create_error_response(
        self,
        status_code: int,
        message: str,
        error_code: str,
        request_id: str
    ) -> JSONResponse:
        """创建错误响应"""
        return JSONResponse(
            status_code=status_code,
            content={
                "success": False,
                "error": {
                    "code": error_code,
                    "message": message,
                    "status_code": status_code
                },
                "request_id": request_id
            },
            headers={
                "X-Request-ID": request_id
            }
        )


def setup_request_validation_middleware(app):
    """设置请求验证中间件"""
    rate_limiter = RateLimiter()
    app.add_middleware(RequestValidationMiddleware, rate_limiter=rate_limiter)
    logger.info("✅ 请求验证中间件已启用")